package com.dayatang.koala.service.impl;

import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.rules.RuleRuntime;
import javax.rules.RuleServiceProvider;
import javax.rules.RuleServiceProviderManager;
import javax.rules.RuleSession;
import javax.rules.StatelessRuleSession;
import javax.rules.admin.LocalRuleExecutionSetProvider;
import javax.rules.admin.RuleAdministrator;
import javax.rules.admin.RuleExecutionSet;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.lang.time.StopWatch;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.dayatang.koala.service.RuleManagement;
import com.dayatang.koala.service.RuleRuntimeException;
import com.dayatang.koala.support.RuleExecutionSetCreator;
import com.dayatang.koala.support.RuleExecutionSetInputStreamCreator;
import com.dayatang.koala.support.RuleExecutionSetObjectCreator;
import com.dayatang.koala.support.RuleExecutionSetReaderCreator;

public class RuleManagementJSR94 implements RuleManagement {

	protected static Log log = LogFactory.getLog(RuleManagementJSR94.class);

	private static RuleExecutionSetInputStreamCreator inputStreamCreator;

	private static RuleExecutionSetObjectCreator objCreator;

	private static RuleExecutionSetReaderCreator readerCreator;

	private final static String JSR94_PROPERTIES = "jsr94.properties";

	private static Configuration config;

	private static String RULE_SERVICE_PROVIDER = "org.drools.jsr94.rules.RuleServiceProviderImpl";

	private static String RULE_SERVICE_PROVIDER_URI = "http://drools.org/";

	public RuleManagementJSR94() {
		if (inputStreamCreator == null) {
			inputStreamCreator = new RuleExecutionSetInputStreamCreator();
		}
		if (objCreator == null) {
			objCreator = new RuleExecutionSetObjectCreator();
		}
		if (readerCreator == null) {
			readerCreator = new RuleExecutionSetReaderCreator();
		}
		try {
			config = new PropertiesConfiguration(JSR94_PROPERTIES);
		} catch (ConfigurationException e) {
			throw new RuleRuntimeException(e);
		}

		RULE_SERVICE_PROVIDER = config.getString("ruleServiceProvider");
		RULE_SERVICE_PROVIDER_URI = config.getString("ruleServiceProvider.uri");

		log.info("The rule service provider of JSR94 is "
				+ RULE_SERVICE_PROVIDER);

	}

	public void setInputStreamCreator(
			RuleExecutionSetInputStreamCreator inputStreamCreator) {
		this.inputStreamCreator = inputStreamCreator;
	}

	public void setObjCreator(RuleExecutionSetObjectCreator objCreator) {
		this.objCreator = objCreator;
	}

	public void setReaderCreator(RuleExecutionSetReaderCreator readerCreator) {
		this.readerCreator = readerCreator;
	}

	public List executeStatelessRules(InputStream ruleSource, Map ruleProperty,
			Map map, List params) throws RuleRuntimeException {

		return executeStatelessSessionRules(ruleSource, ruleProperty, params, map,
				inputStreamCreator);

	}

	public List executeStatelessRules(Reader ruleSource, Map ruleProperty, Map map,
			List params) throws RuleRuntimeException {

		return executeStatelessSessionRules(ruleSource, ruleProperty, params, map,
				readerCreator);

	}

	public List executeStatelessRules(Object ruleSource, Map ruleProperty, Map map,
			List params) throws RuleRuntimeException {

		return executeStatelessSessionRules(ruleSource, ruleProperty, params, map,
				objCreator);

	}

	public List executeStatelessRules(String ruleSource, Map ruleProperty, Map map,
			List params) throws RuleRuntimeException {

		Reader drlReader = new StringReader(ruleSource);

		return executeStatelessSessionRules(drlReader, ruleProperty, params, map,
				readerCreator);

	}

	private List executeStatelessSessionRules(Object ruleSource, Map ruleProperty,
			List params, Map map,
			RuleExecutionSetCreator ruleExecutionSetCreator) {

		List list = new ArrayList();

		StopWatch watch = null;

		if (log.isDebugEnabled()) {
			watch = new StopWatch();
			watch.start();
		}

		try {

			StatelessRuleSession statelessSession = assembleStatelessRuleSession(
					ruleSource, ruleProperty, map, ruleExecutionSetCreator);

			list = statelessSession.executeRules(params);

		} catch (Exception ex) {

			throw new RuleRuntimeException(ex);

		}

		if (log.isDebugEnabled()) {
			watch.stop();
			log.debug("Rule executed in " + watch
					+ " (including parse rule and execution.)");
			// for gc
			watch = null;
		}

		return list;
	}

	public StatelessRuleSession assembleStatelessRuleSession(
			InputStream ruleSource, Map ruleProperty, Map map)
			throws RuleRuntimeException {

		return assembleStatelessRuleSession(ruleSource, ruleProperty, map,
				inputStreamCreator);

	}

	public StatelessRuleSession assembleStatelessRuleSession(Reader ruleSource,
			Map ruleProperty, Map map) throws RuleRuntimeException {

		return assembleStatelessRuleSession(ruleSource, ruleProperty, map,
				readerCreator);

	}

	public StatelessRuleSession assembleStatelessRuleSession(Object ruleSource,
			Map ruleProperty, Map map) throws RuleRuntimeException {

		return assembleStatelessRuleSession(ruleSource, ruleProperty, map,
				objCreator);

	}

	public StatelessRuleSession assembleStatelessRuleSession(String ruleSource,
			Map ruleProperty, Map map) throws RuleRuntimeException {

		Reader drlReader = new StringReader(ruleSource);
		return assembleStatelessRuleSession(ruleSource, ruleProperty, map,
				readerCreator);

	}

	private StatelessRuleSession assembleStatelessRuleSession(
			Object ruleSource, Map ruleProperty, Map map,
			RuleExecutionSetCreator ruleExecutionSetCreator)
			throws RuleRuntimeException {

		return (StatelessRuleSession) assembleRuleSession(ruleSource,
				ruleProperty, map, ruleExecutionSetCreator,
				RuleRuntime.STATELESS_SESSION_TYPE);

	}

	private RuleSession assembleRuleSession(Object ruleSource,
			Map ruleProperty, Map map, RuleExecutionSetCreator creator,
			int ruleSessionType) throws RuleRuntimeException {

		RuleSession ruleSession = null;

		StopWatch watch = null;

		if (log.isDebugEnabled()) {
			watch = new StopWatch();
			watch.start();
		}

		try {

			Class.forName(RULE_SERVICE_PROVIDER);

			RuleServiceProvider ruleServiceProvider = RuleServiceProviderManager
					.getRuleServiceProvider(RULE_SERVICE_PROVIDER_URI);

			RuleAdministrator ruleAdministrator = ruleServiceProvider
					.getRuleAdministrator();

			LocalRuleExecutionSetProvider ruleExecutionSetProvider = ruleAdministrator
					.getLocalRuleExecutionSetProvider(null);

			RuleExecutionSet ruleExecutionSet = creator.createRuleExecutionSet(
					ruleSource, ruleProperty, ruleExecutionSetProvider);

			String packageName = ruleExecutionSet.getName();

			ruleAdministrator.registerRuleExecutionSet(packageName,
					ruleExecutionSet, null);

			RuleRuntime ruleRuntime = ruleServiceProvider.getRuleRuntime();

			ruleSession = ruleRuntime.createRuleSession(packageName, map,
					ruleSessionType);

		} catch (Exception e) {

			throw new RuleRuntimeException(
					"Could not assemble the rule session." + e);

		}

		if (log.isDebugEnabled()) {
			watch.stop();
			log.debug("Rule session Assembled in " + watch);
			// for gc
			watch = null;
		}

		return ruleSession;

	}

}
